package org.hikari.qqbot.xiuxian.task.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.hikari.qqbot.xiuxian.command.ICommand;
import org.hikari.qqbot.xiuxian.command.standard.xuanshang.ChaKanZongMenXuanShangCommand;
import org.hikari.qqbot.xiuxian.command.standard.xuanshang.JieZongMenXuanShangCommand;
import org.hikari.qqbot.xiuxian.command.standard.xuanshang.LingZongMenXuanShangCommand;
import org.hikari.qqbot.xiuxian.command.standard.xuanshang.ShuaXinZongMenXuanShangCommand;
import org.hikari.qqbot.xiuxian.service.bo.UserSetting;
import org.hikari.qqbot.xiuxian.task.TaskParams;
import org.hikari.qqbot.xiuxian.task.TypedITask;
import org.hikari.qqbot.xiuxian.task.UserTaskRuntime;
import org.hikari.qqbot.xiuxian.util.BizUtil;
import org.hikari.qqbot.xiuxian.util.MessageUtil;
import org.hikari.qqbot.xiuxian.util.TimedCache;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * created at 2024-12-26 15:40:41
 *
 * @author yinchao
 * @version 1.0
 **/
@Slf4j
@Component
public class ZongMenXuanShangTask extends TypedITask<ZongMenXuanShangTask.Config> {

    @Override
    public String getId() {
        return "宗门悬赏";
    }

    @Override
    public Collection<ICommand> process(UserTaskRuntime runtime, String message) {
        UserSetting setting = runtime.getUserSetting();
        Config config = config(setting);

        // 如果有待领任务，先领奖励
        if (message.contains("已领任务") && message.contains("待领奖励")) {
            return List.of(new LingZongMenXuanShangCommand());
        }

        // 领完奖励后查看悬赏
        if (message.contains("领宗门悬赏成功")) {
            return List.of(new ChaKanZongMenXuanShangCommand());
        }

        // 分析悬赏内容进行后续操作
        if (message.contains("已领任务") && message.contains("待接任务")) {
            String part = MessageUtil.matchFirstPattern(message, "已领任务\\(([0-9]+/[0-9]+)\\)", 1);
            if (part != null) {
                List<String> arr = StrUtil.split(part, "/");
                int receiveNum = Integer.parseInt(arr.get(0));
                int totalNum = Integer.parseInt(arr.get(1));
                // 总可以数量
                int totalRemainNum = totalNum - receiveNum;
                // 解析待领任务
                Collection<DaiLingXuanShang> daiLingXuanShangList = parseDaiLingXuanShang(message);
                // 解析已领任务
                Collection<YiLingXuanShang> yiLingXuanShangList = parseYiLingXuanShang(message);
                // 根据配置过滤任务
                Collection<DaiLingXuanShang> filtered = filterAndSortDaiLingXuanShangs(daiLingXuanShangList, config);
                // 下次自然刷新时间
                Date nextRefreshTime = parseNextRefreshTime(message);
                // 已领任务中最快的剩余分钟数
                int remainMinute = yiLingXuanShangList.stream()
                        .mapToInt(xs -> xs.getRemainMinutes() == null ? -1 : xs.getRemainMinutes())
                        .filter(minute -> minute >= 0)
                        .min()
                        .orElse(-1);
                // 最短任务完成时间
                Date nextFinishedTime = remainMinute == -1 ? null : DateUtil.offset(new Date(), DateField.MINUTE, remainMinute + 1);
                // 当前可以领的数量 = 3 - 已领数量
                int remainNum = 3 - yiLingXuanShangList.size();
                runtime.getMeta().set(TaskParams.xuanShangRemainNum, remainNum);

                log.info("[{} - {}] 查看宗门悬赏解析结果 \n总剩余次数: {} \n当前剩余次数: {} \n最快完成时间: {} \n下次自然刷新时间: {} \n已领任务: {} \n待领任务: {} \n符合条件的任务: {}",
                        setting.getUserId(), getId(),
                        totalRemainNum, remainNum,
                        MessageUtil.getDisplaySendTime(nextFinishedTime), MessageUtil.getDisplaySendTime(nextRefreshTime),
                        yiLingXuanShangList,
                        daiLingXuanShangList,
                        filtered
                );

                // 没有领的次数领，明日再领
                if (totalRemainNum <= 0) {
                    ICommand command = new ChaKanZongMenXuanShangCommand().delay(BizUtil.beginOfTomorrow().offset(DateField.MINUTE, 1));
                    return List.of(command);
                }

                // 当前可领数量不够了
                if (remainNum == 0) {
                    // 如果有符合条件的悬赏, 但是由于可领数量不够了无法领取, 那么以任务最快完成时间为准
                    if (nextFinishedTime != null && !filtered.isEmpty()) {
                        // 最小完成时间大于下次刷新时间，已完成时间为准
                        if (nextFinishedTime.before(nextRefreshTime)) {
                            log.info("[{} - {}] 已领3个任务，最短完成时间[{}] < 下次任务刷新时间[{}] 以最短完成时间为准, 等待{}分钟后再查看, 下次刷新时间: {}",
                                    setting.getUserId(), getId(),
                                    MessageUtil.getDisplaySendTime(nextFinishedTime),
                                    MessageUtil.getDisplaySendTime(nextRefreshTime),
                                    remainMinute + 1, nextFinishedTime);
                            return List.of(new ChaKanZongMenXuanShangCommand().delay(nextFinishedTime));
                        }
                    }
                    // 延迟指定时间发送查看宗门悬赏
                    return List.of(new ChaKanZongMenXuanShangCommand().delay(nextRefreshTime));
                }

                // 没有符合条件的任务，尝试刷新
                if (filtered.isEmpty()) {
                    ICommand command = buildShuaXinZongMenXuanShangCommand(runtime, config);
                    if (command != null) {
                        return List.of(command);
                    }
                    // 延迟指定时间发送查看宗门悬赏
                    return List.of(new ChaKanZongMenXuanShangCommand().delay(nextRefreshTime));
                }

                // 生成接悬赏命令
                List<ICommand> commands = filtered.stream()
                        // 限定接悬赏的数量不超过当前可领数量
                        .limit(Math.min(remainNum, filtered.size()))
                        .map(xs -> new JieZongMenXuanShangCommand(xs.getSeq()))
                        .collect(Collectors.toList());

                // 接完悬赏后查看宗门悬赏
                commands.add(new ChaKanZongMenXuanShangCommand());
                return commands;
            }
        }

        // 刷新任务后查看
        if (message.contains("刷新成功") && message.contains("新任务")) {
            // 可以接的数量不够了
            Integer remainNum = runtime.getMeta().get(TaskParams.xuanShangRemainNum);
            if (remainNum == null) {
                remainNum = 3;
            }
            if (remainNum <= 0) {
                log.info("[{} - {}] 当前可领数量为0, 不进行任何操作", setting.getUserId(), getId());
                return null;
            }
            // 解析待领任务
            Collection<DaiLingXuanShang> daiLingXuanShangList = parseDaiLingXuanShang(message);
            // 根据配置过滤任务
            List<DaiLingXuanShang> filtered = filterAndSortDaiLingXuanShangs(daiLingXuanShangList, config);
            // 没有符合的并且刷新次数没到上限，再次刷新
            if (filtered.isEmpty()) {
                ICommand command = buildShuaXinZongMenXuanShangCommand(runtime, config);
                if (command != null) {
                    return List.of(command);
                }
            }
            // 生成接悬赏命令
            List<ICommand> commands = filtered.stream()
                    // 限定接悬赏的数量不超过当前可领数量
                    .limit(Math.min(remainNum, filtered.size()))
                    .map(xs -> new JieZongMenXuanShangCommand(xs.getSeq()))
                    .collect(Collectors.toList());
            // 接完悬赏后查看悬赏
            commands.add(new ChaKanZongMenXuanShangCommand());
            return commands;
        }

        if (message.contains("你已有3个正在进行中的悬赏")) {
            runtime.getMeta().set(TaskParams.xuanShangRemainNum, 0);
            return null;
        }

        return null;
    }

    private static ICommand buildShuaXinZongMenXuanShangCommand(UserTaskRuntime runtime, Config config) {
        Date now = new Date();
        if (config.isAutoRefresh()
                // 刷新开始时间 < 当前时间
                && DateUtil.beginOfDay(now).offset(DateField.HOUR, config.getRefreshBeginTime()).before(now)
        ) {
            TimedCache<Integer> shuaXinCountCache = runtime.getMeta().getTimed(TaskParams.xuanShangRefreshTimeCount);
            // 上一次刷新时间如果小于当天开始时间, 那么重置次数
            Integer shuaXinCount = shuaXinCountCache.getValue();
            if (shuaXinCount == null) {
                shuaXinCount = 0;
            }
            // 刷新次数小于等于上限，继续刷
            if (shuaXinCount <= config.getRefreshMaxTimes()) {
                runtime.getMeta().setTimed(TaskParams.xuanShangRefreshTimeCount, shuaXinCount + 1, BizUtil.beginOfTomorrow());
                return new ShuaXinZongMenXuanShangCommand();
            }
        }
        return null;
    }

    private static Date parseNextRefreshTime(String message) {
        String nextTimeStr = MessageUtil.matchFirstPattern(message, "下次自动刷新时间：([0-9- :]+)", 1);
        Date nextTime;
        if (nextTimeStr == null) {
            nextTime = DateUtil.offset(new Date(), DateField.MINUTE, 30);
            log.info("无法解析下次自动刷新时间, 设置成默认值30分钟, 下次刷新时间: {}", MessageUtil.getDisplaySendTime(nextTime));
        } else {
            // 设置1分钟的延迟, 避免时差问题
            nextTime = DateUtil.offsetMinute(DateUtil.parseDateTime(nextTimeStr), 1);
        }
        return nextTime;
    }

    private static List<DaiLingXuanShang> filterAndSortDaiLingXuanShangs(Collection<DaiLingXuanShang> daiLingXuanShangList, Config config) {
        return daiLingXuanShangList.stream()
                .filter(xuanshan -> {
                    double score = xuanshan.scope(config.getTypeWeight());
                    return score >= config.getScoreThreshold();
                })
                .sorted(Comparator.<DaiLingXuanShang>comparingDouble(xs -> xs.scope(config.getTypeWeight())).reversed())
                .toList();
    }

    public static Collection<YiLingXuanShang> parseYiLingXuanShang(String message) {
        List<List<String>> processingList = MessageUtil.matchAllPatternGroup(message, "([0-9]):([\\u4e00-\\u9fa5]+)[\\s]+>[\\s]+任务难度([⭐]+)[\\s]+(剩余[0-9]+分钟|待领奖励)");
        Map<Integer, YiLingXuanShang> dupMap = new LinkedHashMap<>();
        processingList.forEach(r -> {
            YiLingXuanShang info = new YiLingXuanShang();
            info.setSeq(Integer.parseInt(r.get(0)));
            info.setName(r.get(1));
            info.setStar(StrUtil.count(r.get(2), "⭐"));
            String minuteStr = r.get(3);
            if (minuteStr != null) {
                if (minuteStr.contains("待领奖励")) {
                    info.setRemainMinutes(0);
                } else {
                    String mStr = MessageUtil.matchFirstPattern(minuteStr, "[0-9]+");
                    info.setRemainMinutes(Integer.parseInt(mStr));
                }
            }
            dupMap.put(info.getSeq(), info);
        });
        return dupMap.values();
    }

    public static Collection<DaiLingXuanShang> parseDaiLingXuanShang(String message) {
        List<List<String>> waitingList = MessageUtil.matchAllPatternGroup(message, "([0-9]):([\\u4e00-\\u9fa5]+)[\\s]+>[\\s]+任务难度([⭐]+)[\\s]+需要时间[0-9]+分钟[\\s]+奖励：基础灵力×[0-9]+A(\\+|\\s)([\\u4e00-\\u9fa5]+×[0-9]+A)?");
        Map<Integer, DaiLingXuanShang> dupMap = new LinkedHashMap<>();
        waitingList.forEach(r -> {
            DaiLingXuanShang info = new DaiLingXuanShang();
            info.setSeq(Integer.parseInt(r.get(0)));
            info.setName(r.get(1));
            info.setStar(StrUtil.count(r.get(2), "⭐"));
            String typeStr = r.get(4);
            if (StrUtil.isNotBlank(typeStr)) {
                String type = MessageUtil.matchFirstPattern(typeStr, "([\\u4e00-\\u9fa5]+)");
                info.setType(type);
            }
            dupMap.putIfAbsent(info.getSeq(), info);
        });
        return dupMap.values();
    }

    @Override
    public ICommand initCommand(UserSetting setting) {
        return new ChaKanZongMenXuanShangCommand();
    }

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class YiLingXuanShang {

        private int seq;
        private String name;
        private int star;
        private Integer remainMinutes;

        @Override
        public String toString() {
            return seq + ". " + name + " " + "⭐".repeat(Math.max(0, star)) + " (" + remainMinutes + "分钟)";
        }
    }

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class DaiLingXuanShang {

        private int seq;
        private String name;
        private int star;
        private String type;

        public double scope(Map<String, Double> weightMap) {
            double weight;
            if (this.type == null) {
                weight = 1d;
            } else {
                weight = weightMap.getOrDefault(this.type, 1D);
            }
            return star * 1d * weight;
        }

        @Override
        public String toString() {
            return seq + ". " + name + " " + "⭐".repeat(Math.max(0, star)) + " (" + (type == null ? "无" : type) + ")";
        }
    }

    @Getter
    @Setter
    @ToString
    public static class Config {

        /**
         * 悬赏权重
         */
        private Map<String, Double> typeWeight = Map.of(
                "妖兽令", 1.03d,
                "催熟符", 1.02d,
                "十连", 1.01d,
                "金丹", 0.79d
        );
        /**
         * 悬赏分数阈值
         */
        private double scoreThreshold = 4;
        /**
         * 从配置的小时开始允许自动刷新宗门悬赏
         */
        private int refreshBeginTime = 12;
        /**
         * 自动刷新
         */
        private boolean autoRefresh = false;
        /**
         * 最大刷新次数
         */
        private int refreshMaxTimes = 10;

    }


}
